package com.eta.common.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.function.Function;

/**
 * @author 星河一叶Ck
 * @date 2025/9/7
 * @description
 */

@Component
@Slf4j
public class JwtUtils {
    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.refresh-token-expiration}")
    private long refreshTokenExpiration;

    // ------------------------------ getter方法（供外部使用） ------------------------------
    @Getter
    @Value("${jwt.token-prefix}")
    private String tokenPrefix;

    @Getter
    @Value("${jwt.header}")
    private String header;

    @Value("${jwt.expire-minutes}")
    @Getter
    private long expireTime;

    /**
     * 生成默认令牌(包含用户Id)
     * @param userId 用户唯一标识
     * @return 令牌
     */
    public String generateToken(String userId) {
        return generateToken(userId, new HashMap<>());
    }

    public String generateToken(String userId, HashMap<String, Object> claims) {
        // 计算过期时间： 当前时间 + 过期分钟数
        Date expireDate = new Date(System.currentTimeMillis() + expireTime * 60 * 1000);

        // 生成令牌
        return Jwts.builder()
                // 存放自定义数据(claims)
                .setClaims(claims)
                // 主题 （通常放用户ID）
                .setSubject(userId)
                // 签发时间
                .setIssuedAt(new Date())
                // 过期时间
                .setExpiration(expireDate)
                // 签发算法和密钥（HS256需要256位密钥）
                .signWith(getSecretKey(), SignatureAlgorithm.HS256)
                // 压缩并转为字符串
                .compact();
    }

    /**
     * 生成刷新令牌（过期时间更长）
     */
    public String generateRefreshToken(String userId) {
        Date expireDate = new Date(System.currentTimeMillis() + refreshTokenExpiration * 60 * 1000);
        return Jwts.builder()
                .setSubject(userId)
                .setIssuedAt(new Date())
                .setExpiration(expireDate)
                .signWith(getSecretKey(), SignatureAlgorithm.HS256)
                .compact();
    }

    /**
     * 从令牌中获取用户ID
     */
    public String getUserIdFormToken(String token) {
        return getClaimFormToken(token, Claims::getSubject);
    }

    // ------------------------------ 私有工具方法 ------------------------------

    /**
     * 将配置文件中的secret字符串转换为JWT所需的SecretKey对象
     * （HS256算法要求密钥长度至少256位=32字节）
     */
    private SecretKey getSecretKey() {
        // 将字符串密钥转换为字节数组（UTF-8编码）
        byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8);
        return Keys.hmacShaKeyFor(keyBytes);
    }

    /**
     * 从令牌中获取claims
     * @param token 令牌
     * @param claimsResolver 获取claims的解析器
     * @return 解析后的claims
     * @param <T> 返回类型
     */
    private<T> T getClaimFormToken(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = getAllClaimsFromToken(token);
        return claimsResolver.apply(claims);
    }

    /**
     * 解析令牌， 获取所有Claims（需要密钥)
     * @param token 令牌
     * @return  Claims
     */
    private Claims getAllClaimsFromToken(String token) {
        return Jwts.parserBuilder()
                .setSigningKey(getSecretKey())
                .build()
                .parseClaimsJws(token)
                .getBody();
    }

    /**
     * 检查令牌是否过期
     * @param token 令牌
     * @return true：过期
     */
    public boolean isTokenExpired(String token) {
        final Date expiration = getClaimFormToken(token, Claims::getExpiration);
        return expiration.before(new Date());
    }

}
